//      TITLE("Interval and Profile Clock Interrupts")
//++
//
// Copyright (c) 1991  Microsoft Corporation
//
// Module Name:
//
//    x4clock.s
//
// Abstract:
//
//    This module implements the code necessary to field and process the
//    interval and profile clock interrupts on a MIPS R4000 system.
//
// Author:
//
//    David N. Cutler (davec) 26-Apr-1991
//
// Environment:
//
//    Kernel mode only.
//
// Revision History:
//
//--

#include "halmips.h"

#if defined(_DUO_)

#include "duodef.h"

#endif

#if defined(_JAZZ_)

#include "jazzdef.h"

#endif

        SBTTL("System Clock Interrupt - Processor 0")
//++
//
// Routine Description:
//
//    This routine is entered as the result of an interrupt generated by
//    the interval timer. Its function is to acknowledge the interrupt and
//    transfer control to the standard system routine to update the system
//    time and the execution time of the current thread and process.
//
// Arguments:
//
//    s8 - Supplies a pointer to a trap frame.
//
// Return Value:
//
//    None.
//
//--

        .struct 0
CiArgs: .space  4 * 4                   // saved arguments
        .space  3 * 4                   // fill
CiRa:   .space  4                       // saved return address
CiFrameLength:                          //

        NESTED_ENTRY(HalpClockInterrupt0, CiFrameLength, zero)

        subu    sp,sp,CiFrameLength     // allocate stack frame
        sw      ra,CiRa(sp)             // save return address

        PROLOGUE_END

        .set    noreorder

#if defined(_DUO_)

        lw      t0,DMA_VIRTUAL_BASE + 0x58 // acknowledge timer interrupt

#endif

#if defined(_JAZZ_)

        lw      t0,DMA_VIRTUAL_BASE + 0x230 // acknowledge timer interrupt

#endif

        .set    reorder

        move    a0,s8                   // set address of trap frame
        lw      a1,HalpCurrentTimeIncrement // set current time increment
        lw      t0,__imp_KeUpdateSystemTime // update system time
        jal     t0                      //

//
// The following code is a work around for a bug in the Fusion machines
// where the clock interrupt is not dismissed by reading the acknowledge
// register.
//

#if defined(_JAZZ_)

        .set    noreorder
        .set    noat
        mfc0    t0,cause                // read the cause register
        lw      t1,HalpEisaControlBase  // get EISA control base address
        sll     t0,t0,31 - (CAUSE_INTPEND + CLOCK_LEVEL - 1) // isolate clock bit
        bgez    t0,10f                  // if gez, no clock interrupt pending
        li      t2,0x2                  // get NMI port enable bit
        lb      t3,0x70(t1)             // save EISA NMI interrupt disable
        lb      t4,0x461(t1)            // save EISA extended NMI status
        sb      zero,0x70(t1)           // clear EISA NMI interrupt disable
        sb      t2,0x461(t1)            // set EISA NMI port enable
        sb      zero,0x462(t1)          // generate EISA NMI interrupt
        sb      zero,0x461(t1)          // clear EISA extended NMI status
        sb      t2,0x461(t1)            //
        lb      zero,0x461(t1)          // synchronize clear operatin
        sb      t3,0x70(t1)             // restore EISA NMI interupt disable
        sb      t4,0x461(t1)            // restore EISA exteneed NMI status
        lb      zero,0x461(t1)          // synchronize restore operation
        .set    at
        .set    reorder

10:                                     //

#endif

//
// At each clock interrupt the next time increment is moved to the current
// time increment to "pipeline" the update of the current increment at the
// correct time. If the next interval count is nonzero, then the new time
// increment is moved to the next time increment and the next interval count
// register is loaded with the specified interval count minus one (i.e., ms).
//

        lw      t0,KdDebuggerEnabled    // get address of debugger enable
        lw      t1,HalpNextIntervalCount // get next interval count
        lw      t2,HalpNextTimeIncrement // get the next increment value
        lbu     t0,0(t0)                // get debugger enable flag
        lw      t3,HalpNewTimeIncrement // get new new time increment value
        lw      ra,CiRa(sp)             // restore return address
        or      t4,t1,t0                // set interval count or debugger?
        sw      t2,HalpCurrentTimeIncrement // set current increment value
        bne     zero,t4,20f             // if ne, interval change or debugger
        addu    sp,sp,CiFrameLength     // deallocate stack frame
        j       ra                      // return

//
// The interval count must be changed or the debugger is enabled.
//

20:     sw      zero,HalpNextIntervalCount // clear next interval count
        beq     zero,t1,30f             // if eq, not interval count change
        subu    t1,t1,1                 // compute millisecond interval count

        .set    noreorder

#if defined(_DUO_)

        sw      t1,DMA_VIRTUAL_BASE + 0x1a8 // set next interval count

#endif

#if defined(_JAZZ_)

        sw      t1,DMA_VIRTUAL_BASE + 0x228 // set next interval count

#endif

        .set    reorder

        sw      t3,HalpNextTimeIncrement // set next time increment value
30:     beq     zero,t0,40f             // if eq, debugger not enabled
        jal     KdPollBreakIn           // check if breakin is requested
        beq     zero,v0,40f             // if eq, no breakin requested
        li      a0,DBG_STATUS_CONTROL_C // break in and send
        jal     DbgBreakPointWithStatus //  status to debugger
40:     lw      ra,CiRa(sp)             // restore return address
        addu    sp,sp,CiFrameLength     // deallocate stack frame
        j       ra                      // return

        .end    HalpClockInterrupt0

        SBTTL("System Clock Interrupt - Processor N")
//++
//
// Routine Description:
//
//    This routine is entered as the result of an interrupt generated by
//    the interval timer. Its function is to acknowledge the interrupt
//    and transfer control to the standard system routine to update the
//    execution time of the current thread and process.
//
// Arguments:
//
//    s8 - Supplies a pointer to a trap frame.
//
// Return Value:
//
//    None.
//
//--

        LEAF_ENTRY(HalpClockInterrupt1)

#if defined(_DUO_)

        lw      t0,DMA_VIRTUAL_BASE + 0x58 // acknowledge timer interrupt
        move    a0,s8                   // set address of trap frame
        lw      t1,__imp_KeUpdateRunTime // update system runtime
        j       t1                      //

#else

        j       ra                      //

#endif

        .end    HalpClockInterrupt1

        SBTTL("Profile Clock Interrupt")
//++
//
// Routine Description:
//
//    This routine is entered as the result of an interrupt generated by the
//    profile clock. Its function is to acknowledge the profile interrupt,
//    compute the next compare value, update the performance counter, and
//    transfer control to the standard system routine to process any active
//    profiles.
//
// Arguments:
//
//    s8 - Supplies a pointer to a trap frame.
//
// Return Value:
//
//    None.
//
//--

        LEAF_ENTRY(HalpProfileInterrupt)

        .set    noreorder
        .set    noat
        mfc0    t1,count                // get current count value
        mfc0    t0,compare              // get current comparison value
        addu    t1,t1,8                 // factor in lost cycles
        subu    t1,t1,t0                // compute initial count value
        mtc0    t0,compare              // dismiss interrupt
        mtc0    t1,count                // set new count register value
        .set    at
        .set    reorder

#if defined(NT_UP)

        la      t1,HalpPerformanceCounter // get performance counter address

#else

        lw      t1,KiPcr + PcPrcb(zero) // get current processor block address
        la      t2,HalpPerformanceCounter // get performance counter address
        lbu     t1,PbNumber(t1)         // get processor number
        sll     t1,t1,3                 // compute address of performance count
        addu    t1,t1,t2                //

#endif

        lw      t2,LiLowPart(t1)        // get low part of performance count
        lw      t3,LiHighPart(t1)       // get high part of performance count
        addu    t2,t2,t0                // update low part of performance count
        sw      t2,LiLowPart(t1)        // store low part of performance count
        sltu    t4,t2,t0                // generate carry into high part
        addu    t3,t3,t4                // update high part of performance count
        sw      t3,LiHighPart(t1)       // store high part of performance count
        move    a0,s8                   // set address of trap frame
        lw      t4,__imp_KeProfileInterrupt // process profile interrupt
        j       t4                      //

        .end    HalpProfileInterrupt

        SBTTL("Read Count Register")
//++
//
// ULONG
// HalpReadCountRegister (
//    VOID
//    );
//
// Routine Description:
//
//    This routine reads the current value of the count register and
//    returns the value.
//
// Arguments:
//
//    None.
//
// Return Value:
//
//    Current value of the count register.
//
//--

        LEAF_ENTRY(HalpReadCountRegister)

        .set    noreorder
        .set    noat
        mfc0    v0,count                // get count register value
        .set    at
        .set    reorder

        j       ra                      // return

        .end    HalpReadCountRegister

        SBTTL("Write Compare Register And Clear")
//++
//
// ULONG
// HalpWriteCompareRegisterAndClear (
//    IN ULONG Value
//    );
//
// Routine Description:
//
//    This routine reads the current value of the count register, writes
//    the value of the compare register, clears the count register, and
//    returns the previous value of the count register.
//
// Arguments:
//
//    Value - Supplies the value written to the compare register.
//
// Return Value:
//
//    Previous value of the count register.
//
//--

        LEAF_ENTRY(HalpWriteCompareRegisterAndClear)

        .set    noreorder
        .set    noat
        mfc0    v0,count                // get count register value
        mtc0    a0,compare              // set compare register value
        li      t0,7                    // set lost cycle count
        mtc0    t0,count                // set count register to zero
        .set    at
        .set    reorder

        j       ra                      // return

        .end    HalpWriteCompareRegisterAndClear
